package io.zbus.data.impl;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import io.zbus.data.kit.JsonKit;

public class TableKit {
	private static final Pattern WHERE = Pattern.compile("[\\s]+WHERE[\\s]+");  
	
	/**
	 * Parse Table.Column format, if no dot in string, parsed as column only
	 * @param s
	 * @return
	 */
	public static TableColumn tableColumn(String s) {
		String[] bb = s.split("[.]");
		if(bb.length > 2) {
			throw new IllegalArgumentException("Illegal table.column format: " + s);
		}
		TableColumn tc = new TableColumn();
		if(bb.length == 2) {
			tc.table = bb[0].trim();
			tc.column = bb[1].trim();
		} else {
			tc.column = bb[0].trim();
		}
		return tc;
	}
	
	/**
	 * Check if where populated in SQL
	 * @param sql
	 * @return
	 */
	public static boolean whereExists(String sql) {
		sql = sql.toUpperCase();
		return WHERE.matcher(sql).find(); 
	}  
	
	public static Object get(Object p, String name) {
		//getter
		String getterName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
		Method getter = null;
		try {
			getter = p.getClass().getMethod(getterName);
			if(getter != null) {
				return getter.invoke(p);
			}
		} catch (Exception e) { 
			//ignore
		}   
		
		//field
		try {
			Field f = p.getClass().getField(name);
			return f.get(p);
		} catch (Exception e) { 
			//ignore
		}  
		
		//if map
		if(p instanceof Map) {
			@SuppressWarnings("unchecked")
			Map<String, Object> m = (Map<String, Object>)p;
			return m.get(name);
		}
		return null;
	}
	
	public static void set(Object p, String name, Object value) {
		//setter
		String setterName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
		Method setter = null;
		try {
			setter = p.getClass().getMethod(setterName, Object.class);
			if(setter != null) { 
				value = JsonKit.convert(value, setter.getParameterTypes()[0]);  //change type
				setter.invoke(p, value);
				return;
			}
		} catch (Exception e) { 
			//ignore
		}   
		
		//field
		try {
			Field f = p.getClass().getField(name); 
			value = JsonKit.convert(value, f.getType());  //change type
			f.set(p, value);
			return;
		} catch (Exception e) { 
			//ignore
		}  
		
		//if map
		if(p instanceof Map) {
			@SuppressWarnings("unchecked")
			Map<String, Object> m = (Map<String, Object>)p;
			m.put(name, value);
		} 
	}
	
	public static Set<String> findKeys(List<Object> data, String[] paths) { 
		if(paths.length < 1) {
			return new HashSet<>();
		} 
		List<Object> leafList = new ArrayList<>();
		findByPath(leafList, data, paths, 0);
		
		Set<String> res = new HashSet<>();
		for(Object m : leafList) { 
			res.add(m.toString()); 
		}
		return res;
	} 
	
	public static void findByPath(List<Object> res, List<Object> dataList, String[] paths, int currentLayer){
		if(currentLayer >= paths.length) { 
			res.addAll(dataList);
			return; 
		}
		
		String path = paths[currentLayer];
		if(currentLayer == paths.length-1) { 
			for(Object data : dataList) {
				Object value = get(data, path);
				if(value == null) continue;
				
				if(value instanceof List) {
					@SuppressWarnings("unchecked")
					List<Object> listValue = (List<Object>)value;
					res.addAll(listValue);
				} else {
					res.add(value);
				}
			}
			return;
		}  
		
		List<Object> newDataList = new ArrayList<>(); 
		for(Object data : dataList) {
			Object value = get(data, path);
			if(value == null) continue;
			
			if(value instanceof List) {
				@SuppressWarnings("unchecked")
				List<Object> listValue = (List<Object>)value;
				newDataList.addAll(listValue);
			} else {
				newDataList.add(value);
			}
		}
		
		findByPath(res, newDataList, paths, currentLayer+1);
	} 
	
	public static class TableColumn {
		public String table;
		public String column;
		
		public String fullName() {
			return table + "." + column;
		}
	}  
}
